;================================================================
;  main.s
;               Main module of 25thanni
;
;================================================================
;
; 25thanni, a demo dedicated to the 25th anniversary of the ZX81.
;
; (c)2006 Bodo Wenzel
;
; This program is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License as
; published by the Free Software Foundation; either version 2 of
; the License, or (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with this program; if not, write to the Free
; Software Foundation Inc., 59 Temple Place, Suite 330, Boston,
; MA 02111-1307 USA
;================================================================

	.module	main

;= Externals ====================================================

	.globl	s_TRAILER

	.globl	delay
	.globl	matrix
	.globl	fire
	.globl	jump
	.globl	magnify
	.globl	wobble
	.globl	hopper
	.globl	end

	.globl	heap_ptr
	.globl	show_ptr
	.globl	bottom_ptr
	.globl	frames
	.globl	break

	.globl	ZX_FONT_I_REG

	.globl	DELAY_OFFSET

	.globl	G0,G1,G2,G3,G4,G5,G6,G7,G8,G9,GA,GB,GC,GD,GE,GF
	.globl	__,X8,X9,XA,QU,PD,DL,CL,QM,LP,RP,GT,LT,EQ,PL,MI
	.globl	TI,SL,SC,CM,PE,_0,_1,_2,_3,_4,_5,_6,_7,_8,_9
	.globl	_A,_B,_C,_D,_E,_F,_G,_H,_I,_J,_K,_L,_M,_N,_O,_P
	.globl	_Q,_R,_S,_T,_U,_V,_W,_X,_Y,_Z,NL,INV

;= Constants ====================================================

FRAME_RATE	==	50	; frames per second
M_TOP		==	46	; margin before standard show
ROWS_VISIBLE	==	192	; number of visible pixel rows
M_BOTTOM	==	5	; margin after standard show
M_VSYNC		=	46	; margin before vsync
SCROLL_LENGTH	=	47	; number of visible characters

;= Input and output =============================================

NMI_OFF		=	0xFD	;via OUT
NMI_ON		=	0xFE	;via OUT
VSYNC_ON	=	0xFE	;via IN
VSYNC_OFF	=	0xFF	;via OUT

;= Program code =================================================

	.area	CODE

	ld	hl,#show_dummy
	call	i_set_show
	ld	ix,#dsp_loop	; control the display now!

	ld	hl,#s_TRAILER+0xFF
	ld	l,#0
	ld	(heap_ptr),hl	; set up paged heap pointer

main_repeat:
	ld	hl,#table
main_loop:
	ld	e,(hl)
	inc	hl
	ld	d,(hl)
	inc	hl
	ld	a,e
	or	d
	jr	z,main_repeat

	push	hl
	ld	hl,#main_ret
	push	hl
	ex	de,hl
	jp	(hl)
main_ret:
	call	check_break
	pop	hl
	jr	c,main_loop

	call	end
	jr	main_repeat

;- Check for BREAK ----------------------------------------------

check_break::
	ld	a,(break)
	rra
	ret

;- Installs a show routine --------------------------------------

set_show::
	ex	de,hl
	call	vsync
	ex	de,hl		; ensure correct timing
i_set_show:
	ld	e,(hl)
	inc	hl
	ld	d,(hl)
	inc	hl
	ld	(bottom_ptr),de
	ld	(show_ptr),hl
	ret

;- Dummy screen routine -----------------------------------------

show_dummy::
	.dw	empty_scroll
	.db	M_TOP+ROWS_VISIBLE
	.db	M_BOTTOM

	ret

;- The bottom scroller ------------------------------------------

show_scroller:
	; The delay is carefully adjusted via an oscilloscope!
	ld	a,(frames+0)
	rrca
	cpl
	and	#0x03
	add	#158+DELAY_OFFSET
	call	delay

	ld	hl,#scrolltext+0x8000
	push	hl
	push	hl
	push	hl
	push	hl
	push	hl
	push	hl
	push	hl
	scf
	jp	(hl)
scrolltext:
	.db	0,0,0,0,0,0,0,0,0,0
	.db	0,0,0,0,0,0,0,0,0,0
	.db	0,0,0,0,0,0,0,0,0,0
	.db	0,0,0,0,0,0,0,0,0,0
	.db	0,0,0,0,0,0,0,0,0	; 47 characters visible!
	ret	c		; 49*4+11 = 207

incr_scroller:			; 1274 clocks including call
	ld	a,(frames+0)	; 13
	cpl			; 4
	and	#0x07		; 7
	jr	z,is_scroll	; 12 / 7
	ld	b,#81		; 7
	djnz	.		; 80*13+8
	ld	a,#34		; = 1086+34
	jp	delay		; (10)

is_scroll:			; 36
	ld	hl,#scrolltext+1; 10
	ld	de,#scrolltext	; 10
	ld	bc,#SCROLL_LENGTH; 10
	ldir			; 46*21+16=982
	ld	hl,(bottom_ptr)	; 16
	ld	a,(hl)		; 7
	cp	#NL		; 7
	jr	z,is_last	; 12 / 7
	inc	hl		; 6
	ld	(bottom_ptr),hl	; 16
	dec	de		; 6
	ld	(de),a		; 7
	ld	a,#0		; = 1120
	jp	delay		; (10)

is_last:			; 1090
	ld	a,#30		; = 1090+30
	jp	delay		; (10)

;- The NMI code in the ROM, just for reference ------------------

;nmi:
;	ex	af,af'
;	inc	a
;	jp	m,nmi_quit
;	jr	z,nmi_break
;nmi_quit:
;	ex	af,af'
;	ret
;
;nmi_break:
;	ex	af,af'
;	push	af
;	push	bc
;	push	de
;	push	hl
;	ld	hl,(D_FILE)
;	set	7,h
;	halt
;	out	(NMI_OFF),a
;	jp	(ix)

;- Vertical synchronisation and retrace -------------------------

call_show:
	ld	hl,(show_ptr)
	inc	hl
	inc	hl
	jp	(hl)		; call installed show routine

dsp_loop:
	ld	hl,(show_ptr)
	ld	a,(hl)
	call	call_main	; gap before show

	call	call_show

	ld	a,#ZX_FONT_I_REG
	ld	i,a		; the scroller uses text mode

	ld	hl,(show_ptr)
	inc	hl
	ld	a,(hl)
	call	call_main	; gap after show

	ld	hl,(frames)
	inc	hl
	ld	(frames),hl

	call	show_scroller

	ld	a,#M_VSYNC
	call	call_main	; gap after bottom scroller

	; delay to near end of line
	ld	b,#8
	djnz	.

	; the vsync pulse is 6 lines wide, plus one hsync pulse
	; clocks to go:	6 * 207 + 15 = 1257
	ld	a,#0x7F
	in	a,(VSYNC_ON)	; 11/2
	ld	(break),a	; 13
	call	incr_scroller	; 1274	too much... but OK
	out	(VSYNC_OFF),a	; 11/2

	jr	dsp_loop

;- Return to the main code --------------------------------------

call_main::
	pop	ix		; get the return address

	neg
	ex	af,af'		; the NMI is incrementing!

	out	(NMI_ON),a

	pop	hl
	pop	de
	pop	bc
	pop	af
	ret			; back to the main code

;- Wait for a number of frames ----------------------------------
; Uses AF and HL

vsync::
	ld	a,#1
wait_frames::
	ld	hl,#frames+0
	add	a,(hl)
wf_loop:
	cp	(hl)
	jr	nz,wf_loop
	ret

;- Check whether the bottom scroller has finished ---------------
; Uses AF and HL
; Returns Z set if bottom scroller has finished

b_check::
	ld	hl,(bottom_ptr)
	ld	a,(hl)
	cp	#NL
	ret

;= Data =========================================================

table:
	.dw	matrix
	.dw	fire
	.dw	jump
	.dw	magnify
	.dw	wobble
	.dw	hopper
	.dw	0

empty_scroll:
	.db	NL

;= Heap usage ===================================================

; This segment is just for a memory check. Each module that uses
; the heap has to allocate the needed space. The map file of the
; linker shows the total size, which should be checked against
; the available space beginning at (heap_ptr) and ending at the
; stack (for sanity: 0x7FC0).

	.area	HEAP	(abs,ovr)

;= The end ======================================================
